//! 哈希索引测试
//! 
//! 测试哈希索引的核心功能

#[cfg(test)]
mod tests {
    use super::super::hash::*;
    use crate::sql::Value;
    use std::collections::HashSet;

    #[test]
    fn test_hash_basic_operations() {
        let mut hash_index = HashIndex::new(16); // 16个桶
        
        // 测试插入
        assert!(hash_index.insert(Value::Integer(10), 1).is_ok());
        assert!(hash_index.insert(Value::Integer(20), 2).is_ok());
        assert!(hash_index.insert(Value::Text("hello".to_string()), 3).is_ok());
        assert!(hash_index.insert(Value::Text("world".to_string()), 4).is_ok());
        
        // 测试查找
        assert_eq!(hash_index.lookup(&Value::Integer(10)), Some(1));
        assert_eq!(hash_index.lookup(&Value::Integer(20)), Some(2));
        assert_eq!(hash_index.lookup(&Value::Text("hello".to_string())), Some(3));
        assert_eq!(hash_index.lookup(&Value::Text("world".to_string())), Some(4));
        assert_eq!(hash_index.lookup(&Value::Integer(100)), None);
        
        // 测试删除
        assert!(hash_index.delete(&Value::Integer(10)).is_ok());
        assert_eq!(hash_index.lookup(&Value::Integer(10)), None);
        assert_eq!(hash_index.lookup(&Value::Integer(20)), Some(2));
    }
    
    #[test]
    fn test_hash_collision_handling() {
        let mut hash_index = HashIndex::new(4); // 小桶数，容易产生冲突
        
        // 插入多个可能冲突的键
        for i in 0..20 {
            hash_index.insert(Value::Integer(i), i as usize).unwrap();
        }
        
        // 验证所有键都能正确查找
        for i in 0..20 {
            assert_eq!(hash_index.lookup(&Value::Integer(i)), Some(i as usize));
        }
        
        // 测试删除后的查找
        for i in 0..10 {
            hash_index.delete(&Value::Integer(i)).unwrap();
        }
        
        for i in 0..10 {
            assert_eq!(hash_index.lookup(&Value::Integer(i)), None);
        }
        for i in 10..20 {
            assert_eq!(hash_index.lookup(&Value::Integer(i)), Some(i as usize));
        }
    }
    
    #[test]
    fn test_hash_duplicate_keys() {
        let mut hash_index = HashIndex::new(8);
        
        // 插入重复键
        assert!(hash_index.insert(Value::Integer(10), 1).is_ok());
        assert!(hash_index.insert(Value::Integer(10), 2).is_ok());
        assert!(hash_index.insert(Value::Integer(10), 3).is_ok());
        
        // 查找应该返回其中一个值
        let result = hash_index.lookup(&Value::Integer(10));
        assert!(result.is_some());
        let value = result.unwrap();
        assert!(value == 1 || value == 2 || value == 3);
        
        // 获取所有匹配的值
        let all_values = hash_index.lookup_all(&Value::Integer(10));
        assert_eq!(all_values.len(), 3);
        assert!(all_values.contains(&1));
        assert!(all_values.contains(&2));
        assert!(all_values.contains(&3));
    }
    
    #[test]
    fn test_hash_resize() {
        let mut hash_index = HashIndex::new(4); // 开始时桶数较少
        
        // 插入大量数据，触发扩容
        for i in 0..100 {
            hash_index.insert(Value::Integer(i), i as usize).unwrap();
        }
        
        // 验证扩容后所有数据仍然可以找到
        for i in 0..100 {
            assert_eq!(hash_index.lookup(&Value::Integer(i)), Some(i as usize));
        }
        
        // 检查负载因子是否合理
        let load_factor = hash_index.load_factor();
        assert!(load_factor < 0.75); // 负载因子应该小于0.75
    }
    
    #[test]
    fn test_hash_performance() {
        let mut hash_index = HashIndex::new(1024); // 大桶数，减少冲突
        let n = 10000;
        
        // 测试插入性能
        let start = std::time::Instant::now();
        for i in 0..n {
            hash_index.insert(Value::Integer(i), i as usize).unwrap();
        }
        let insert_time = start.elapsed();
        
        // 测试查找性能
        let start = std::time::Instant::now();
        for i in 0..n {
            assert_eq!(hash_index.lookup(&Value::Integer(i)), Some(i as usize));
        }
        let lookup_time = start.elapsed();
        
        // 测试删除性能
        let start = std::time::Instant::now();
        for i in 0..n/2 {
            hash_index.delete(&Value::Integer(i)).unwrap();
        }
        let delete_time = start.elapsed();
        
        println!("哈希索引性能测试 ({}条记录):", n);
        println!("插入时间: {:?}", insert_time);
        println!("查找时间: {:?}", lookup_time);
        println!("删除时间: {:?}", delete_time);
        
        // 性能要求（哈希索引应该比B+树更快）
        assert!(insert_time.as_millis() < 500); // 插入应该在0.5秒内完成
        assert!(lookup_time.as_millis() < 200); // 查找应该在0.2秒内完成
        assert!(delete_time.as_millis() < 300); // 删除应该在0.3秒内完成
    }
    
    #[test]
    fn test_hash_string_keys() {
        let mut hash_index = HashIndex::new(16);
        
        let test_strings = vec![
            "apple", "banana", "cherry", "date", "elderberry",
            "fig", "grape", "honeydew", "kiwi", "lemon"
        ];
        
        // 插入字符串键
        for (i, s) in test_strings.iter().enumerate() {
            hash_index.insert(Value::Text(s.to_string()), i).unwrap();
        }
        
        // 验证查找
        for (i, s) in test_strings.iter().enumerate() {
            assert_eq!(hash_index.lookup(&Value::Text(s.to_string())), Some(i));
        }
        
        // 测试不存在的键
        assert_eq!(hash_index.lookup(&Value::Text("mango".to_string())), None);
    }
    
    #[test]
    fn test_hash_concurrent_access() {
        use std::sync::{Arc, Mutex};
        use std::thread;
        
        let hash_index = Arc::new(Mutex::new(HashIndex::new(64)));
        let mut handles = vec![];
        
        // 多线程并发插入
        for thread_id in 0..4 {
            let index_clone = hash_index.clone();
            let handle = thread::spawn(move || {
                for i in 0..100 {
                    let key = thread_id * 100 + i;
                    let mut index = index_clone.lock().unwrap();
                    index.insert(Value::Integer(key), key as usize).unwrap();
                }
            });
            handles.push(handle);
        }
        
        // 等待所有线程完成
        for handle in handles {
            handle.join().unwrap();
        }
        
        // 验证所有数据都插入成功
        let index = hash_index.lock().unwrap();
        for thread_id in 0..4 {
            for i in 0..100 {
                let key = thread_id * 100 + i;
                assert_eq!(index.lookup(&Value::Integer(key)), Some(key as usize));
            }
        }
    }
    
    #[test]
    fn test_hash_memory_usage() {
        let mut hash_index = HashIndex::new(32);
        let n = 1000;
        
        // 插入数据
        for i in 0..n {
            hash_index.insert(Value::Integer(i), i as usize).unwrap();
        }
        
        // 检查内存使用情况
        let memory_usage = hash_index.memory_usage();
        println!("哈希索引内存使用: {} bytes", memory_usage);
        
        // 内存使用应该合理
        assert!(memory_usage > 0);
        assert!(memory_usage < 1024 * 1024); // 应该小于1MB
    }
    
    #[test]
    fn test_hash_statistics() {
        let mut hash_index = HashIndex::new(16);
        
        // 插入一些数据
        for i in 0..50 {
            hash_index.insert(Value::Integer(i), i as usize).unwrap();
        }
        
        let stats = hash_index.get_statistics();
        
        assert_eq!(stats.total_entries, 50);
        assert_eq!(stats.bucket_count, 16);
        assert!(stats.load_factor > 0.0);
        assert!(stats.max_chain_length > 0);
        assert!(stats.avg_chain_length > 0.0);
        
        println!("哈希索引统计信息:");
        println!("总条目数: {}", stats.total_entries);
        println!("桶数量: {}", stats.bucket_count);
        println!("负载因子: {:.2}", stats.load_factor);
        println!("最大链长度: {}", stats.max_chain_length);
        println!("平均链长度: {:.2}", stats.avg_chain_length);
    }
    
    #[test]
    fn test_hash_clear() {
        let mut hash_index = HashIndex::new(8);
        
        // 插入数据
        for i in 0..20 {
            hash_index.insert(Value::Integer(i), i as usize).unwrap();
        }
        
        // 验证数据存在
        assert_eq!(hash_index.size(), 20);
        
        // 清空索引
        hash_index.clear();
        
        // 验证索引已清空
        assert_eq!(hash_index.size(), 0);
        for i in 0..20 {
            assert_eq!(hash_index.lookup(&Value::Integer(i)), None);
        }
    }
}
